package osmedile.intellij.checkmyvars;

import com.intellij.codeInsight.template.impl.TemplateContext;
import com.intellij.codeInsight.template.impl.TemplateEditorUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import static com.intellij.openapi.util.text.StringUtil.isEmptyOrSpaces;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * Dialog to create new VarChecker
 *
 * @author Olivier Smedile
 * @version $Id$
 */
public class AddVarCheckerDialog extends JDialog {
    {
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
        $$$setupUI$$$();
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * >>> IMPORTANT!! <<<
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private void $$$setupUI$$$() {
        contentPane = new JPanel();
        contentPane.setLayout(new GridBagLayout());
        contentPane.setMinimumSize(new Dimension(400, 400));
        contentPane.setOpaque(true);
        contentPane.setPreferredSize(new Dimension(800, 600));
        contentPane.setRequestFocusEnabled(true);
        final JPanel panel1 = new JPanel();
        panel1.setLayout(new GridBagLayout());
        GridBagConstraints gbc;
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.weighty = 0.1;
        gbc.fill = GridBagConstraints.BOTH;
        contentPane.add(panel1, gbc);
        buttonOK = new JButton();
        buttonOK.setText("OK");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.EAST;
        panel1.add(buttonOK, gbc);
        buttonCancel = new JButton();
        buttonCancel.setHorizontalAlignment(0);
        buttonCancel.setText("Cancel");
        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.EAST;
        panel1.add(buttonCancel, gbc);
        final JSplitPane splitPane1 = new JSplitPane();
        splitPane1.setOneTouchExpandable(true);
        splitPane1.setOrientation(0);
        splitPane1.setResizeWeight(0.8);
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 3;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        panel1.add(splitPane1, gbc);
        mainPanel = new JPanel();
        mainPanel.setLayout(new GridBagLayout());
        mainPanel.setMinimumSize(new Dimension(200, 100));
        mainPanel.setPreferredSize(new Dimension(800, 800));
        mainPanel.setRequestFocusEnabled(true);
        splitPane1.setLeftComponent(mainPanel);
        errorLbl = new JLabel();
        errorLbl.setForeground(new Color(-65536));
        errorLbl.setText("Annotation");
        errorLbl.setVisible(false);
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 3;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(errorLbl, gbc);
        final JLabel label1 = new JLabel();
        label1.setText("Annotation");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(label1, gbc);
        final JLabel label2 = new JLabel();
        label2.setRequestFocusEnabled(true);
        label2.setText("Type");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(label2, gbc);
        final JLabel label3 = new JLabel();
        label3.setRequestFocusEnabled(true);
        label3.setText("Group");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 3;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(label3, gbc);
        final JLabel label4 = new JLabel();
        label4.setText("Template");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 4;
        gbc.weighty = 1.0;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(label4, gbc);
        typeTxt = new JTextField();
        typeTxt.setToolTipText(
                "<html>\nType must be a fully qualified Java class name.<br>\nValid substitutions are:<br>\n<b><i>number</b></i> for short, int, long, float, long, double and java.lang.Number.class<br>\n<b><i>byte</b></i> for byte and java.lang.Byte<br>\n<b><i>short</b></i> for short and java.lang.Short<br>\n<b><i>int</b></i> for int and java.lang.Integer<br>\n<b><i>long</b></i> for long and java.lang.Long<br>\n<b><i>float</b></i> for float and java.lang.Float<br>\n<b><i>double</b></i> for double and java.lang.Double<br>\n<b><i>boolean</b></i> for boolean and java.lang.Boolean<br>\n<b><i>char</b></i> and <b><i>character</b></i> for char and java.lang.Character<br>\n<b><i>string</b></i> for java.lang.String<br>\n<b><i>date</b></i> for java.util.Date<br>\n<b><i>collection</b></i> for java.util.Collection<br>\n<b><i>list</b></i> for java.util.List<br>\n</html>");
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 2;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        mainPanel.add(typeTxt, gbc);
        groupTxt = new JTextField();
        groupTxt.setToolTipText(
                "<html>\nType must be a fully qualified Java class name.<br>\nValid substitutions are:<br>\n<b><i>number</b></i> for short, int, long, float, long, double and java.lang.Number.class<br>\n<b><i>byte</b></i> for byte and java.lang.Byte<br>\n<b><i>short</b></i> for short and java.lang.Short<br>\n<b><i>int</b></i> for int and java.lang.Integer<br>\n<b><i>long</b></i> for long and java.lang.Long<br>\n<b><i>float</b></i> for float and java.lang.Float<br>\n<b><i>double</b></i> for double and java.lang.Double<br>\n<b><i>boolean</b></i> for boolean and java.lang.Boolean<br>\n<b><i>char</b></i> and <b><i>character</b></i> for char and java.lang.Character<br>\n<b><i>string</b></i> for java.lang.String<br>\n<b><i>date</b></i> for java.util.Date<br>\n<b><i>collection</b></i> for java.util.Collection<br>\n<b><i>list</b></i> for java.util.List<br>\n</html>");
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 3;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        mainPanel.add(groupTxt, gbc);
        annoExprTxt = new JTextField();
        annoExprTxt.setToolTipText(
                "<html>If starts with '@', then only the name of the annotation is used.<br>\nOtherwise, the fully qualified name is used.</html>");
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        mainPanel.add(annoExprTxt, gbc);
        descPanel = new JPanel();
        descPanel.setLayout(new CardLayout(0, 0));
        descPanel.setBackground(new Color(-723983));
        splitPane1.setRightComponent(descPanel);
        final JLabel label5 = new JLabel();
        label5.setBackground(new Color(-723728));
        label5.setText(
                "<html>\n<h3><u>Annotation</u></h3>\n'@' in annotation name is not mandatory.<br>\n<br>\nWhen the plugin try to match the annotation name, it will first use the fully qualitifed name of the annotation.<br>\nIf it doesn't match, it will use the simple name.<br>\nIf it still doesn't match, this annotation will be ignored.\n</html>");
        label5.setVerticalAlignment(1);
        descPanel.add(label5, "ANNO");
        final JLabel label6 = new JLabel();
        label6.setText(
                "<html>\n<h3><u>Type</u></h3>\nType must be a fully qualified Java class name or a following substitutions keywords:<br>\nA primitive name<br>\n<b><i>number</b></i> for short, int, long, float, long, double and java.lang.Number class<br>\n<b><i>string</b></i> for java.lang.String<br>\n<b><i>date</b></i> for java.util.Date<br>\n<b><i>array</b></i> for Arrays<br>\n<b><i>varargs</b></i> for Varargs<br>\n<b><i>collection</b></i> for java.util.Collection<br>\n<b><i>list</b></i> for java.util.List<br>\n<b><i>set</b></i> for java.util.Set<br>\n<br>\nNote that, a primitive name, will match the primitive and its wrapper class.<br>\nFor example: <b><i>int</b></i> match \"int\" and \"java.lang.Integer\". <b><i>boolean</b></i> match \"boolean\" and \"java.lang.Boolean\", ...\n</html>");
        label6.setVerticalAlignment(1);
        descPanel.add(label6, "TYPE");
        final JLabel label7 = new JLabel();
        label7.setText(
                "<html>\n<h3><u>Group name</u></h3>\nA simple name to separate annotation.<br>\n</html>");
        label7.setVerticalAlignment(1);
        descPanel.add(label7, "GROUP");
        final JLabel label8 = new JLabel();
        label8.setText(
                "<html>\n<h3><u>Template</u></h3><br>\nVariables replacement are:<br>\nCurrent Class: <b><i>$CLASS$</b></i><br>\nMethod: <b><i>$METHOD$</b></i><br>\nVariable name: <b><i>$VAR$</b></i><br>\nVariable type: <b><i>$VAR_TYPE$</b><i><br>\n<br>\nNote that for array and varargs:<br>\n<b><i>$VAR_TYPE$</b></i> is the type of the array content.<br>\n<b><i>$VAR_REALTYPE$</b></i> is the real type of the array (with the [])<br>\n<br>\nFor generics: <b><i>$VAR_REALTYPE$</b></i> is the type with generics.<br>\n<br>\nTo use annotation attributes, you should use the following syntax: \"<b>$A_<i>AnnotatioName</i>_P_<i>AttributeName</i>$</b>\"<br>\nFor example, for annotation @MinMax, attribute \"max\" value is: $A_MinMax_P_max$\n</html>");
        label8.setVerticalAlignment(1);
        descPanel.add(label8, "TEMPLATE");
    }

    /**
     * @noinspection ALL
     */
    public JComponent $$$getRootComponent$$$() {
        return contentPane;
    }


    public enum DESC_CARDS {
        ANNO, TYPE, GROUP, TEMPLATE
    }

    private JTextField annoExprTxt;

    private JPanel mainPanel;
    private JTextField typeTxt;
    private JLabel errorLbl;
    private VarChecker varChecker;

    private Editor templateEditor;

    private JPanel contentPane;

    private JButton buttonOK;
    private JButton buttonCancel;
    private JTextField groupTxt;
    private JPanel descPanel;


    private CMVConfig mainPage;

    private boolean isEditMode = false;

// --------------------------- CONSTRUCTORS ---------------------------

    public AddVarCheckerDialog() {
        setContentPane(contentPane);
        setModal(true);
        getRootPane().setDefaultButton(buttonOK);

        buttonOK.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onOK();
            }
        });

        buttonCancel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onCancel();
            }
        });

        // call onCancel() when cross is clicked
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                onCancel();
            }
        });

        varChecker = new VarChecker();

        // call onCancel() on ESCAPE
        contentPane.registerKeyboardAction(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onCancel();
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

        //ensure editor exists
        getTemplateEditor();

        //Focus listener

        annoExprTxt.addFocusListener(new FocusAdapter() {
            public void focusGained(FocusEvent e) {
                ((CardLayout) descPanel.getLayout()).show(descPanel, DESC_CARDS.ANNO.name());
            }
        });

        typeTxt.addFocusListener(new FocusAdapter() {
            public void focusGained(FocusEvent e) {
                ((CardLayout) descPanel.getLayout()).show(descPanel, DESC_CARDS.TYPE.name());
            }
        });

        groupTxt.addFocusListener(new FocusAdapter() {
            public void focusGained(FocusEvent e) {
                ((CardLayout) descPanel.getLayout()).show(descPanel, DESC_CARDS.GROUP.name());
            }
        });

        getTemplateEditor().getContentComponent().addFocusListener(new FocusAdapter() {
            public void focusGained(FocusEvent e) {
                ((CardLayout) descPanel.getLayout()).show(descPanel, DESC_CARDS.TEMPLATE.name());
            }
        });
    }

    private void onOK() {
        if (isEmptyOrSpaces(getTemplateEditor().getDocument().getText())) {
            showError("Template can't be empty");
        } else if (isEmptyOrSpaces(typeTxt.getText()) && isEmptyOrSpaces(annoExprTxt.getText())) {
            showError("Either annotation or type must be filled");
        } else {
            resetErrors();
            //add current values as a new VarChecker
            linkToVarChecker();

            if (getIsEditMode()) {
                //varChecker is updated
                mainPage.getAnnotationTableModel().fireTableDataChanged();
                dispose();
            } else {
                mainPage.getAnnotationTableModel().add(getVarChecker());
                dispose();
            }
        }
    }

    public boolean getIsEditMode() {
        return isEditMode;
    }

    public void setEditMode(boolean editMode) {
        isEditMode = editMode;
    }

    public void showError(String text) {
        errorLbl.setText(text);
        errorLbl.setVisible(true);
    }

    public void resetErrors() {
        errorLbl.setText("");
        errorLbl.setVisible(false);
    }

    protected void linkToVarChecker() {
        varChecker.setAnnotation(annoExprTxt.getText());
        varChecker.setTemplate(getTemplateEditor().getDocument().getText());
        varChecker.setGroup(groupTxt.getText());
        varChecker.setType(typeTxt.getText());
    }


    private void onCancel() {
        if (templateEditor != null) {
            EditorFactory.getInstance().releaseEditor(templateEditor);
            mainPanel.remove(getTemplateEditor().getComponent());
            templateEditor = null;
        }
        dispose();
    }

    // --------------------- GETTER / SETTER METHODS ---------------------

    public VarChecker getVarChecker() {
        return varChecker;
    }

    public CMVConfig getMainPage() {
        return mainPage;
    }

    public void setVarChecker(final VarChecker varChecker) {
        this.varChecker = varChecker;
        groupTxt.setText(varChecker.getGroup());
        annoExprTxt.setText(varChecker.getAnnotation());
        typeTxt.setText(varChecker.getType());


        ApplicationManager.getApplication().runWriteAction(new Runnable() {
            public void run() {
                getTemplateEditor().getDocument().setText(varChecker.getTemplate());
            }
        });
    }

    public Editor getTemplateEditor() {
        if (templateEditor == null) {
            templateEditor = EditorFactory.getInstance().createEditor(
                    EditorFactory.getInstance().createDocument(""));
            templateEditor.getComponent().setPreferredSize(new Dimension(250, 100));
            templateEditor.getSettings().setFoldingOutlineShown(false);
            templateEditor.getSettings().setLineNumbersShown(true);
            templateEditor.getSettings().setLineMarkerAreaShown(false);

            TemplateEditorUtil.setHighlighter(templateEditor, new TemplateContext());

            //UI
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 1;
            gbc.gridy = 4;
            gbc.weightx = 1.0;
            gbc.weighty = 1.0;
            gbc.fill = GridBagConstraints.BOTH;
            gbc.anchor = GridBagConstraints.WEST;
            mainPanel.add(getTemplateEditor().getComponent(), gbc);
        }
        return templateEditor;
    }

    public void setMainPage(CMVConfig CMVConfig) {
        this.mainPage = CMVConfig;
    }

// -------------------------- OTHER METHODS --------------------------

    public void reset() {
        varChecker = new VarChecker();
        annoExprTxt.setText("");
        typeTxt.setText("");

        ApplicationManager.getApplication().runWriteAction(new Runnable() {
            public void run() {
                getTemplateEditor().getDocument().setText("");
            }
        });
    }


    private void createUIComponents() {
    }


}
